/*
 * Licensed to Nanjing RedOrange Co. ltd (RedOrange) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * RedOrange licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package redora.impl;

import java.io.File;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com.mysql.jdbc.JDBC4Connection;

import redora.exceptions.ConnectException;
import redora.configuration.Configuration;
import redora.configuration.DatabaseType;

/**
 * Central connection manager that connects to the database as set up in
 * /etc/redora/configuration.xml.
 * This factory primarily loads and caches the configuration file.
 *
 * @author Redora
 */
public class DatabaseFactory {

    private static final Logger l = Logger.getLogger("redora.impl.Database");
    /** Database configuration file etc/redora/configuration.xml. */
    public static final String CONFIGURATION_FILE = File.separator + "etc"
        + File.separator + "redora" + File.separator
        + "configuration.xml";
    private static DatabaseType config;

    private DatabaseFactory() {
    }

    /** Loads and caches the configuration file. */
    public static DatabaseType configuration() {
        if (config == null) {
            l.log(Level.FINE, "Loading configuration");
            try {
                JAXBContext jc = JAXBContext
                     .newInstance("redora.configuration");
                Unmarshaller u = jc.createUnmarshaller();
                Configuration conf = (Configuration) u.unmarshal(new File(
                    CONFIGURATION_FILE));
                config = conf.getDatabase();
            } catch (JAXBException e) {
                l.log(
                    Level.SEVERE,
                    "Cannot open the configuration file, or"
                    + "the configuratiopn file is not valid",
                    e);
            }
        }
        return config;
    }

    /** Get database connections. */
    public static JDBC4Connection connect() throws ConnectException {
        JDBC4Connection retval;
        try {
            if (configuration().getJdbcConnection() != null) {
                l.log(Level.FINE, "JDBC connect with: "
                    + configuration().getJdbcConnection().getDatabase());
                Properties info = new Properties();
                info.put("user", configuration().getJdbcConnection()
                        .getUsername());
                info.put("password", configuration()
                        .getJdbcConnection().getPassword());
                info.put("runConfig", "maxPerformance");
                retval = (JDBC4Connection) DriverManager.getConnection(
                    "jdbc:mysql://"
                    + configuration().getJdbcConnection().getHost()
                    + "/"
                    + configuration().getJdbcConnection().getDatabase()
                    + "?useUnicode=true&characterEncoding=utf8", info);
            } else {
                l.log(Level.FINE, "Datasource connection: "
                    + configuration().getDatasourceConnection());
                Context ctx = new InitialContext();
                DataSource ds = (DataSource) ctx.lookup(configuration()
                        .getDatasourceConnection());
                retval = (JDBC4Connection) ds.getConnection();
            }

            l.fine("Connected");
        } catch (SQLException e) {
            l.log(Level.SEVERE, "Failed to connect to database "
                + configuration().getJdbcConnection().getUsername() + "@"
                + configuration().getJdbcConnection().getHost() + "/"
                + configuration().getJdbcConnection().getDatabase(), e);
            throw new ConnectException("Failed to connect to database "
                + configuration().getJdbcConnection().getUsername() + "@"
                + configuration().getJdbcConnection().getHost() + "/"
                + configuration().getJdbcConnection().getDatabase(), e);
        } catch (NamingException e) {
            l.log(Level.SEVERE, "Cannot find or open datasource "
                + configuration().getDatasourceConnection(), e);
            throw new ConnectException("Cannot find or open datasource "
                + configuration().getDatasourceConnection(), e);
        }

        return retval;
    }
}
